using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using Gtk;

using Gazebo.Core;

using Mono.Addins;

namespace Gazebo
{
    [System.ComponentModel.ToolboxItem(true)]
    public partial class IcsConsole : Gtk.Bin, IChunkConsumer
    {
        private static readonly Regex continuationRegex = new Regex(" *\n\\\\   +");
        
        private List<ILineHighlighter> highlighters = new List<ILineHighlighter>();
        
        private TextTag sentTag;
        
        private TextTag informationalTag;
        
        public IcsConsole()
        {
            this.Build();
            
            consoleView.ModifyFont(Pango.FontDescription.FromString("Monospace 10"));
            consoleView.ModifyBase(StateType.Normal, new Gdk.Color(0, 0, 0));
            consoleView.ModifyText(StateType.Normal, new Gdk.Color(255, 255, 255));
            
            sentTag = new TextTag(null);
            sentTag.ForegroundGdk = new Gdk.Color(64, 64, 64);
            consoleView.Buffer.TagTable.Add(sentTag);
            
            informationalTag = new TextTag(null);
            informationalTag.ForegroundGdk = new Gdk.Color(192, 255, 192);
            consoleView.Buffer.TagTable.Add(informationalTag);
            
            AddinManager.AddExtensionNodeHandler("/Gazebo/IcsConsole/LineHighlighter", HighlighterNodeChanged);
        }
        
        private void HighlighterNodeChanged(object sender, ExtensionNodeEventArgs e)
        {
            TypeExtensionNode node = (TypeExtensionNode) e.ExtensionNode;
            ILineHighlighter highlighter = node.GetInstance() as ILineHighlighter;
            
            if (highlighter == null) {
                return;
            }
            
            if (e.Change == ExtensionChange.Add) {
                consoleView.Buffer.TagTable.Add(highlighter.Tag);
                highlighters.Add(highlighter);
            } else {
                consoleView.Buffer.TagTable.Remove(highlighter.Tag);
                highlighters.Remove(highlighter);
            }
        }
        
        void IChunkConsumer.ConsumeChunk(Chunk chunk)
        {
            if (chunk.LoggedIn) {
                AppendChunk(chunk.Content);
            } else {
                AppendRawChunk(chunk.Content);
            }
        }
        
        public void AppendChunk(string chunk)
        {
            chunk = continuationRegex.Replace(chunk, " ").TrimNewlines();
            
            if (chunk == "") {
                return;
            }
            
            TextBuffer buffer = consoleView.Buffer;
            
            TextMark begin = new TextMark(null, true);
            buffer.AddMark(begin, buffer.EndIter);
            
            foreach (string line in chunk.Split('\n')) {
                AppendLine(line, null, AppendOptions.PrependNewline);
            }
            
            TextMark end = new TextMark(null, false);
            buffer.AddMark(end, buffer.EndIter);
            
            foreach (IConsolePostProcessor processor in AddinUtilities.GetExtensionObjects<IConsolePostProcessor>("/Gazebo/IcsConsole/PostProcessor")) {
                processor.PostProcess(buffer, begin, end);
            }
            
            buffer.DeleteMark(begin);
            buffer.DeleteMark(end);
        }
        
        public void AppendRawChunk(string chunk)
        {
            AppendLine(chunk, null, AppendOptions.NoTag);
        }
        
        public void AppendInformation(string text)
        {
            AppendLine(text, informationalTag, AppendOptions.None);
        }
        
        public void AppendSentLine(string line)
        {
            AppendLine("> " + line, sentTag, AppendOptions.PrependNewline);
        }
        
        private void AppendLine(string chunk, TextTag tag, AppendOptions options)
        {
            TextBuffer buffer = consoleView.Buffer;
            
            TextIter end;
            
            if (IsSet(options, AppendOptions.PrependNewline)) {
            	end = buffer.EndIter;
            	buffer.Insert(ref end, "\n");
            }
            
            if (tag == null || !IsSet(options, AppendOptions.NoTag)) {
	            foreach (ILineHighlighter highlighter in highlighters) {
	                if (highlighter.IsHighlighted(chunk)) {
                        tag = highlighter.Tag;
	                    break;
	                }
	            }
            }
            
            end = buffer.EndIter;
            if (tag != null) {
            	buffer.InsertWithTags(ref end, chunk, tag);
            } else {
                buffer.Insert(ref end, chunk);
            }
            
            if (IsSet(options, AppendOptions.AppendNewline)) {
            	end = buffer.EndIter;
            	buffer.Insert(ref end, "\n");
            }
            
            consoleView.Buffer.PlaceCursor(consoleView.Buffer.EndIter);
            consoleView.ScrollToMark(consoleView.Buffer.InsertMark, 0, true, 0, 1);
        }
        
        [Flags]
        private enum AppendOptions {
            None = 0x0,
            PrependNewline = 0x1,
            AppendNewline = 0x2,
            NoTag = 0x4
        }
        
    	private static bool IsSet(AppendOptions v, AppendOptions bit)
        {
            return (v & bit) == bit;
        }
    }
}
